#include "util_file.h"
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <sys/stat.h>

namespace util::file
{
std::string GetLastWriteTime(const std::string& file_path)
{
    struct stat file_stat;
    if (stat(file_path.c_str(), &file_stat) == 0)
    {
        std::time_t last_write_time = file_stat.st_mtime;
        std::tm*    time_info       = std::localtime(&last_write_time);

        std::ostringstream oss;
        oss << std::put_time(time_info, "%Y-%m-%d %H:%M:%S");
        return oss.str();
    }
    else
    {
        return "1970-01-01 00:00:00";
    }
}

std::string GetCreateTime(const std::string& file_path)
{
    struct stat file_stat;
    if (stat(file_path.c_str(), &file_stat) == 0)
    {
        std::time_t create_time = file_stat.st_ctime;
        std::tm*    time_info   = std::localtime(&create_time);

        std::ostringstream oss;
        oss << std::put_time(time_info, "%Y-%m-%d %H:%M:%S");
        return oss.str();
    }
    else
    {
        return "1970-01-01 00:00:00";
    }
}

bool EnsureDirectoryExists(const std::string& path)
{
    std::error_code err_code;
    if (!std::filesystem::is_directory(path, err_code))
        return std::filesystem::create_directories(path, err_code);
    return false;
}

bool RemoveDirectory(const std::string& dirpath)
{
    std::error_code err_code;
    if (std::filesystem::is_directory(dirpath, err_code))
        return std::filesystem::remove_all(dirpath, err_code);
    return false;
}

bool ExistsDir(const std::string& dirpath)
{
    std::error_code err_code;
    return std::filesystem::is_directory(dirpath, err_code);
}

bool ExistsFile(const std::string& dirpath)
{
    std::error_code err_code;
    return std::filesystem::exists(dirpath, err_code);
}

bool RemoveFile(const std::string& dirpath)
{
    std::error_code err_code;
    if (std::filesystem::exists(dirpath, err_code))
        return std::filesystem::remove(dirpath, err_code);
    return false;
}

std::string ReadFile(const std::string& filename)
{
    std::ifstream file(filename, std::ios::in | std::ios::binary);
    if (!file)
        return "";

    std::ostringstream buffer;
    buffer << file.rdbuf();
    return buffer.str();
}

bool IsInvalidFile(const std::string& file_name)
{
    if (file_name.empty()) return true;

    return file_name.find("..") != std::string::npos || file_name.find("&") != std::string::npos;
}

std::string GetLatestFile(const std::string& dirpath, const std::string& extension)
{
    std::filesystem::path           lastest_file;
    std::filesystem::file_time_type lastest_time;

    for (const auto& entry : std::filesystem::directory_iterator(dirpath))
    {
        if (entry.is_regular_file() && (extension.empty() ? true : entry.path().extension() == extension))
        {
            auto ftime = std::filesystem::last_write_time(entry);
            if (lastest_file.empty() || ftime > lastest_time)
            {
                lastest_time = ftime;
                lastest_file = entry.path().filename();
            }
        }
    }

    return lastest_file.empty() ? "" : lastest_file;
}

std::streamsize GetFileSize(const std::string& file_path)
{
    std::ifstream file(file_path, std::ios::binary | std::ios::ate);

    if (!file)
    {
        return -1;
    }
    return file.tellg();
}

std::string getDataPath()
{

    return (std::filesystem::canonical(std::filesystem::path("/proc/self/exe")).parent_path() / ".." / "file").string();
}

bool IsZipFile(const std::string& filePath) {
    std::ifstream file(filePath, std::ios::binary);
    if (!file) {
        std::cerr << "无法打开文件: " << filePath << std::endl;
        return false;
    }

    unsigned char magic[4] = {0};
    file.read(reinterpret_cast<char*>(magic), sizeof(magic));

    // ZIP 文件的魔数: 0x50 0x4B 0x03 0x04
    return magic[0] == 0x50 && magic[1] == 0x4B &&
           magic[2] == 0x03 && magic[3] == 0x04;
}
}  // namespace util::file